package ru.ksu.ktk.diploma.sdi.mapping.learner.impl;

import java.util.*;

import ru.ksu.ktk.diploma.sdi.dao.ResourcesDao;
import ru.ksu.ktk.diploma.sdi.mapping.Triple;
import ru.ksu.ktk.diploma.sdi.mapping.learner.*;
import ru.ksu.ktk.diploma.sdi.mapping.learner.impl.InternalModelElement.Builder;
import ru.ksu.ktk.diploma.sdi.mapping.loaders.ThesauriLoader;
import ru.ksu.ktk.diploma.sdi.mapping.thesauri.DescriptorPair;
import ru.ksu.ktk.diploma.sdi.mapping.thesauri.Thesauri;

/**
 * Naive-Bayesian Learner for mapping examples with data source
 * 
 * @author coldflame 04.05.2009 19:51:42
 */
public class MappingLearnerImpl implements MappingLearner {
    /**
     * URI to load learner internal model
     */
    private String uri;
    /**
     * resources DAO
     */
    private ResourcesDao resourcesDao;
    /**
     * stratified by pair of triples example set
     */
    private Map<Example, List<LearningExample>> stratifiedMap;
    /**
     * all descriptor pairs
     */
    private Set<DescriptorPair> allDescriptorPairs;
    /**
     * all descriptor pairs count
     */
    private int allDescriptorPairsCount;

    private List<InternalModelElement> internalModel;

    /* (non-Javadoc)
     * @see ru.ksu.ktk.diploma.sdi.mapping.learner.MappingLearner#learn(java.util.List)
     */
    public void learn(List<LearningExample> learningExamples) {
        internalModel = new ArrayList<InternalModelElement>();
        stratifyExamplesSet(learningExamples);
        Set<Example> exampleKeys = stratifiedMap.keySet();
        for (Example exampleKey : exampleKeys){
            List<LearningExample> examplesByPair = stratifiedMap.get(exampleKey);
            computeProbabilities(examplesByPair);
        }
    }

    public void init() {
        this.allDescriptorPairsCount = allDescriptorPairs.size();
    }

    /* (non-Javadoc)
     * @see ru.ksu.ktk.diploma.sdi.mapping.learner.MappingLearner#apply(ru.ksu.ktk.diploma.sdi.mapping.learner.Example)
     */
    public TriplePrediction apply(Example example) {
        throw new UnsupportedOperationException("not implemented yet");
    }

    public String getUri() {
        return uri;
    }

    public void setUri(String uri) {
        this.uri = uri;
    }

    public ResourcesDao getResourcesDao() {
        return resourcesDao;
    }

    public void setResourcesDao(ResourcesDao resourcesDao) {
        this.resourcesDao = resourcesDao;
    }

    /**
     * group examples by pair of triples
     * 
     * @param learningExamples
     */
    private void stratifyExamplesSet(List<LearningExample> learningExamples) {
        stratifiedMap = new HashMap<Example, List<LearningExample>>();
        for (LearningExample example : learningExamples){
            if (stratifiedMap.containsKey(example)){
                List<LearningExample> examples = stratifiedMap.get(example);
                examples.add(example);
            }
            else{
                List<LearningExample> examples = new ArrayList<LearningExample>();
                examples.add(example);
                stratifiedMap.put(example, examples);
            }
        }
    }

    private void computeProbabilities(List<LearningExample> examplesByPair) {
        int propertiesCount = examplesByPair.size();
        for (LearningExample learningExample : examplesByPair){
            Triple firstTriple = learningExample.getFirstTriple();
            Triple secondTriple = learningExample.getSecondTriple();
            Triple predictedProperty = learningExample.getPredictedProperty();

            float propertyProbability = 1f / propertiesCount;
            List<DescriptorPair> positives = new ArrayList<DescriptorPair>();
            List<DescriptorPair> negatives = new ArrayList<DescriptorPair>();
            positives.addAll(learningExample.getDescriptorPairs());

            for (LearningExample anotherExample : examplesByPair){
                boolean isAnotherExample = !anotherExample.getPredictedProperty().equals(predictedProperty);
                if (isAnotherExample){
                    negatives.addAll(anotherExample.getDescriptorPairs());
                }
            }
            List<ProbableDescriptorPair> probableDescriptorPairs = new ArrayList<ProbableDescriptorPair>();
            int positivesSize = positives.size();
            int negativeSize = negatives.size();
            for (DescriptorPair uniqueDescriptorPair : allDescriptorPairs){
                int positiveCount = Collections.frequency(positives, uniqueDescriptorPair);
                int negativeCount = Collections.frequency(negatives, uniqueDescriptorPair);
                float positiveProbability = ((float) positiveCount + 1) / (positivesSize + allDescriptorPairsCount);
                float negativeProbability = ((float) negativeCount + 1) / (negativeSize + allDescriptorPairsCount);
                probableDescriptorPairs.add(new ProbableDescriptorPair(uniqueDescriptorPair, positiveProbability,
                    negativeProbability));
            }
            InternalModelElement internalModelElement =
                new InternalModelElement.Builder(firstTriple, secondTriple, predictedProperty).propertyProbability(
                    propertyProbability).descriptors(probableDescriptorPairs).build();
            internalModel.add(internalModelElement);
        }
    }
}
